//
//  GaoExceptionManager.m
//  GTMacks
//
//  Created by GikkiAres on 15/1/19.
//  Copyright (c) 2015年 RG-IOS-DEV-002. All rights reserved.
//

#import "JsCrashManager.h"
#include <libkern/OSAtomic.h>
#include <execinfo.h>
#include <stdatomic.h>

void HandleException(NSException *exception);
void SignalHandler(int signal);
void InstallUncaughtExceptionHandler(void);
NSString * const UncaughtExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";
NSString * const UncaughtExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey";
NSString * const UncaughtExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey";

volatile int32_t UncaughtExceptionCount = 0;
const int32_t UncaughtExceptionMaximum = 10;

//开始记录的调用index.
const NSInteger UncaughtExceptionHandlerSkipAddressCount = 0;
//控制获取的地址个数,感觉没什么用.
const NSInteger UncaughtExceptionHandlerReportAddressCount = 20;


@interface JsCrashManager()



@end

@implementation JsCrashManager

// 加载时注册异常捕获的函数.
+ (void)load {
	InstallUncaughtExceptionHandler();
}

static JsCrashManager * _sharedInstance;
+ (JsCrashManager *)sharedInstance {
	static dispatch_once_t onceToken;
	dispatch_once(&onceToken, ^{
		_sharedInstance = [[JsCrashManager alloc] init];
	});
	return _sharedInstance;
}

+ (NSArray *)backtrace
{
    void* callstack[128];
	//获取系统捕获到的异常,返回调用的层次.
    int frames = backtrace(callstack, 128);
    char **strs = backtrace_symbols(callstack, frames);
    
    int i;
    NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];
	//起始点.  //个数  //
    for (i = 0;i < frames;i++) {
		char * str = strs[i];
		if(str != NULL && strlen(str) > 0) {
			[backtrace addObject:[NSString stringWithUTF8String:str]];
		}
		else {
			break;
		}
    }
    free(strs);
    
    return backtrace;
}




+ (void)writefile:(NSString *)string {
	NSString * dirPath = [NSString stringWithFormat:@"%@/%@",NSHomeDirectory(),@"Documents/CrashLog"];
	if(![[NSFileManager defaultManager] fileExistsAtPath:dirPath]) {
		NSError * error = nil;
		[[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:YES attributes:nil error:&error];
		if(error) {
			NSLog(@"Create crash log dir failed.");
		}
		
	}
	

	NSString * filePath = [NSString stringWithFormat:@"%@/%@",dirPath,@"crash.log"];
	if(![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
		[[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
	}
    NSFileHandle *fileHandle = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
    //将节点跳到文件的末尾
    [fileHandle seekToEndOfFile];
    NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
    [dateFormatter setDateFormat:@"[yyyy-MM-dd HH:mm:ss]"];
    NSString *datestr = [dateFormatter stringFromDate:[NSDate date]];
    NSString *str = [NSString stringWithFormat:@"%@\n%@",datestr,string];
    NSData* stringData  = [str dataUsingEncoding:NSUTF8StringEncoding];
    //追加写入数据
    [fileHandle writeData:stringData];
    [fileHandle closeFile];
}

+ (NSString*)dictionaryToJson:(NSDictionary *)dic
{
    NSError *parseError = nil;
    NSData *jsonData = [NSJSONSerialization dataWithJSONObject:dic options:NSJSONWritingPrettyPrinted error:&parseError];
    
    return [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding];
}

- (void)crash1 {
	
	NSMutableArray * marr = [NSMutableArray array];
	[marr addObject:nil];
}

@end

void handleCrashWithDictionary(NSDictionary * dic) {
	id _delegate = [JsCrashManager sharedInstance].delegate;
	if(_delegate && [_delegate respondsToSelector:@selector(jscrashManager:willCrashWithInfo:)]) {
		[_delegate jscrashManager:[JsCrashManager sharedInstance] willCrashWithInfo:dic];
	}
}

//系统会把异常信息作为参数传过来.已经包含很详细的堆栈信息.
void customHandleException(NSException *exception) {
		//获取到的堆栈信息.
	NSArray *callStack = [JsCrashManager backtrace];
	NSMutableDictionary * exceptionInfo =
	[NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];
	exceptionInfo[UncaughtExceptionHandlerAddressesKey] = callStack;
	exceptionInfo[@"name"] = exception.name;
	exceptionInfo[@"reason"] = exception.reason;
	
	handleCrashWithDictionary(exceptionInfo);
}

void SignalHandler(int signal){
    NSMutableDictionary *userInfo =
    [NSMutableDictionary
     dictionaryWithObject:[NSNumber numberWithInt:signal]
     forKey:UncaughtExceptionHandlerSignalKey];

    NSArray *callStack = [JsCrashManager backtrace];
    [userInfo
     setObject:callStack
     forKey:UncaughtExceptionHandlerAddressesKey];
	handleCrashWithDictionary(userInfo);
}

//设置系统异常处理函数
void InstallUncaughtExceptionHandler(void) {
    NSSetUncaughtExceptionHandler(&customHandleException);
    signal(SIGABRT, SignalHandler);
    signal(SIGILL, SignalHandler);
    signal(SIGSEGV, SignalHandler);
    signal(SIGFPE, SignalHandler);
    signal(SIGBUS, SignalHandler);
    signal(SIGPIPE, SignalHandler);
}

